/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SKSL_LAYOUT
#define SKSL_LAYOUT

#include "src/base/SkEnumBitMask.h"

#include <string>

namespace SkSL {
class Context;
class Position;

enum class LayoutFlag : int {
    kNone = 0,
    kAll = ~0,

    kOriginUpperLeft = 1 << 0,
    kPushConstant = 1 << 1,
    kBlendSupportAllEquations = 1 << 2,
    kColor = 1 << 3,

    // These flags indicate if the qualifier appeared, regardless of the accompanying value.
    kLocation = 1 << 4,
    kOffset = 1 << 5,
    kBinding = 1 << 6,
    kTexture = 1 << 7,
    kSampler = 1 << 8,
    kIndex = 1 << 9,
    kSet = 1 << 10,
    kBuiltin = 1 << 11,
    kInputAttachmentIndex = 1 << 12,

    // These flags indicate the backend type; only one at most can be set.
    kVulkan = 1 << 13,
    kMetal = 1 << 14,
    kWebGPU = 1 << 15,
    kDirect3D = 1 << 16,

    kAllBackends = kVulkan | kMetal | kWebGPU | kDirect3D,

    // These flags indicate the pixel format; only one at most can be set.
    // (https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)#Image_formats)
    kRGBA8 = 1 << 17,
    kRGBA32F = 1 << 18,
    kR32F = 1 << 19,

    kAllPixelFormats = kRGBA8 | kRGBA32F | kR32F,

    // The local invocation size of a compute program.
    kLocalSizeX = 1 << 20,
    kLocalSizeY = 1 << 21,
    kLocalSizeZ = 1 << 22,
};
} // namespace SkSL

SK_MAKE_BITMASK_OPS(SkSL::LayoutFlag);

namespace SkSL {
using LayoutFlags = SkEnumBitMask<SkSL::LayoutFlag>;

/* *
 * Represents a layout block appearing before a variable declaration, as in:
 *
 * layout (location = 0) int x;
 */
struct Layout {
    Layout(LayoutFlags flags, int location, int offset, int binding, int index, int set, int builtin,
        int inputAttachmentIndex)
        : fFlags(flags),
          fLocation(location),
          fOffset(offset),
          fBinding(binding),
          fIndex(index),
          fSet(set),
          fBuiltin(builtin),
          fInputAttachmentIndex(inputAttachmentIndex)
    {}

    constexpr Layout() = default;

    static Layout builtin(int builtin)
    {
        Layout result;
        result.fBuiltin = builtin;
        return result;
    }

    std::string description() const;
    std::string paddedDescription() const;

    /* *
     * Verifies that only permitted layout flags are included. Reports errors and returns false in
     * the event of a violation.
     */
    bool checkPermittedLayout(const Context &context, Position pos, LayoutFlags permittedLayoutFlags) const;

    bool operator == (const Layout &other) const;

    bool operator != (const Layout &other) const
    {
        return !(*this == other);
    }

    LayoutFlags fFlags = LayoutFlag::kNone;
    int fLocation = -1;
    int fOffset = -1;
    int fBinding = -1;
    int fTexture = -1;
    int fSampler = -1;
    int fIndex = -1;
    int fSet = -1;
    // builtin comes from SPIR-V and identifies which particular builtin value this object
    // represents.
    int fBuiltin = -1;
    // input_attachment_index comes from Vulkan/SPIR-V to connect a shader variable to the a
    // corresponding attachment on the subpass in which the shader is being used.
    int fInputAttachmentIndex = -1;

    // The local invocation size dimensions of a compute program.
    int fLocalSizeX = -1;
    int fLocalSizeY = -1;
    int fLocalSizeZ = -1;
};
} // namespace SkSL

#endif
